package com.neshai.webapp.service;

import com.google.gson.Gson;
import com.neshai.webapp.entity.EthereumTransaction;
import com.neshai.webapp.mapper.EthereumTransactionMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameterNumber;
import org.web3j.protocol.core.methods.response.*;
import org.web3j.protocol.http.HttpService;
import org.web3j.utils.Numeric;

import java.io.IOException;
import java.math.BigInteger;
import java.time.Instant;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Service
@RequiredArgsConstructor
public class BlockChainInfoService {
    final private Web3j web3j = Web3j.build(new HttpService("https://misty-long-sanctuary.quiknode.pro/b5d44d2c1a992e98e932a593a9790f37365d1869"));

    //private final DelayedTask delayedTask;

    @Value("${eth.receiveAddress}")
    private String receiveAddress;

    private final EthereumTransactionMapper ethereumTransactionMapper;

    private final UpgradeService upgradeService;

    private final ThreadPoolTaskScheduler scheduler;

    /**
     * @description 获取最新的区块号
     * @author newonexd
     * @date 2022/6/22 21:11
     * <p>
     * * @return BigInteger
     */
    public BigInteger doGetLatestBlockNumber() throws Exception {
        EthBlockNumber ethBlockNumber = web3j.ethBlockNumber().sendAsync().get();
        BigInteger blockNumber = ethBlockNumber.getBlockNumber();
        System.out.println("BlockNumber: {}" + blockNumber);
        return blockNumber;
    }

    /**
     * @description 获取所有账户
     * @author newonexd
     * @date 2022/6/22 21:11
     * <p>
     * * @return List<String>
     */
    public List<String> doGetAllAccounts() throws Exception {
        EthAccounts ethAccounts = web3j.ethAccounts().sendAsync().get();
        List<String> accounts = ethAccounts.getAccounts();
        System.out.println("Accounts: {}" + accounts);
        return accounts;
    }

    /**
     * @description 获取Gas价格
     * @author newonexd
     * @date 2022/6/22 21:11
     * <p>
     * * @return BigInteger
     */
    public BigInteger doGetEthGasPrice() throws Exception {
        EthGasPrice ethGasPrice = web3j.ethGasPrice().sendAsync().get();
        return ethGasPrice.getGasPrice();
    }

    /**
     * @description 获取链Id
     * @author newonexd
     * @date 2022/6/22 21:12
     * <p>
     * * @return BigInteger
     */
    public BigInteger doGetChainId() throws Exception {
        EthChainId ethChainId = web3j.ethChainId().sendAsync().get();
        return ethChainId.getChainId();
    }

    /**
     * @description 获取CoinBase
     * @author newonexd
     * @date 2022/6/22 21:12
     * <p>
     * * @return String
     */
    @GetMapping("/coinbase")
    public String doGetCoinBase() throws Exception {
        EthCoinbase ethCoinbase = web3j.ethCoinbase().sendAsync().get();
        return ethCoinbase.getAddress();
    }


    /**
     * @param blockNumber 区块号
     * @return String
     * @description 根据区块号获取区块信息
     * @author newonexd
     * @date 2022/6/22 21:12
     */
    public String doGetAll(Long blockNumber) throws Exception {
        DefaultBlockParameterNumber defaultBlockParameterNumber = new DefaultBlockParameterNumber(blockNumber);
        EthBlock ethBlock = web3j.ethGetBlockByNumber(defaultBlockParameterNumber, true).sendAsync().get();
        EthBlock.Block block = ethBlock.getBlock();
        Gson gson = new Gson();
        String info = gson.toJson(block);
        return info;
    }


    /**
     * @param blockNumber 区块号
     * @return String
     * @description 根据区块号获取所有交易
     * @author newonexd
     * @date 2022/6/22 21:13
     */

    public String doGetTransactionInfoByBlockNumber(Long blockNumber) throws Exception {
        DefaultBlockParameterNumber defaultBlockParameterNumber = new DefaultBlockParameterNumber(blockNumber);
        EthBlock ethBlock = web3j.ethGetBlockByNumber(defaultBlockParameterNumber, true).sendAsync().get();
        List<EthBlock.TransactionResult> transactionResults = ethBlock.getBlock().getTransactions();
        List<Transaction> txInfos = new ArrayList<>();

        transactionResults.forEach(txInfo -> {
            Transaction transaction = (Transaction) txInfo;
            txInfos.add(transaction);
        });
        Gson gson = new Gson();
        String transactionInfo = gson.toJson(txInfos);
        return transactionInfo;
    }


    /**
     * @param txHash 交易哈希值
     * @return String
     * @description 根据交易哈希值获取交易信息
     * @author newonexd
     * @date 2022/6/22 21:13
     */
    public String doGetTransactionInfoByHash(String txHash) throws Exception {
        EthTransaction transaction = web3j.ethGetTransactionByHash(txHash).sendAsync().get();
        Optional<Transaction> optionalTransaction = transaction.getTransaction();
        StringBuilder txInfo = new StringBuilder();
        if (optionalTransaction.isPresent()) {
            Transaction transactionInfo = optionalTransaction.get();
            Gson gson = new Gson();
            txInfo.append(gson.toJson(transactionInfo));
        }
        return txInfo.toString();
    }

    public void checkERC20Transfers(String txHash, Integer id) throws Exception {
        String finalTxHash = txHash.toLowerCase();

        Runnable task = () -> {
            EthereumTransaction ethereumTransaction = ethereumTransactionMapper.selectById(finalTxHash);
            if (ethereumTransaction == null) {
                EthGetTransactionReceipt transactionReceiptResponse = null;
                try {
                    transactionReceiptResponse = web3j.ethGetTransactionReceipt(finalTxHash).send();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
                Optional<TransactionReceipt> transactionReceiptOptional = transactionReceiptResponse.getTransactionReceipt();

                if (transactionReceiptOptional.isPresent()) {
                    TransactionReceipt receipt = transactionReceiptOptional.get();
                    System.out.println(receipt);

                    // 获取所有日志
                    List<Log> logs = receipt.getLogs();

                    // ERC-20 Transfer 事件的主题哈希
                    String transferEventSignature = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";

                    for (Log log : logs) {
                        List<String> topics = log.getTopics();
                        // 检查是否为 Transfer 事件
                        if (topics != null && !topics.isEmpty() && topics.get(0).equals(transferEventSignature) && "0xdAC17F958D2ee523a2206206994597C13D831ec7".equalsIgnoreCase(log.getAddress())) {
                            // 提取 `from`, `to`, `value`
                            String from = "0x" + topics.get(1).substring(26);
                            String to = "0x" + topics.get(2).substring(26);
                            BigInteger value = Numeric.decodeQuantity(log.getData());
                            if (receiveAddress.equalsIgnoreCase(to)) {
                                EthereumTransaction ethereumTransactionInsert = new EthereumTransaction();
                                ethereumTransactionInsert.setHash(finalTxHash);
                                ethereumTransactionInsert.setUserId(id);
                                ethereumTransactionInsert.setIsSuccess((byte) 1);
                                ethereumTransactionInsert.setBlockHash(receipt.getBlockHash());
                                ethereumTransactionInsert.setSenderAddress(from);
                                ethereumTransactionInsert.setRecipientAddress(to);
                                ethereumTransactionInsert.setBlockNumber(receipt.getBlockNumber());
                                ethereumTransactionInsert.setValue(value);
                                ethereumTransactionInsert.setCreatedAt(LocalDateTime.now());
                                ethereumTransactionMapper.insert(ethereumTransactionInsert);
                                upgradeService.paidAndUpgradeByUserId(id, value.longValue());
                            } else {
                                System.out.println("receiveAddress: " + to + "\n not my receive");
                            }
                        } else {
                            System.out.println("topic is null or not a transfer event or contract addr is error");
                            System.out.println(log);
                        }
                    }


                } else {
                    System.out.println("Transaction receipt not found.");
                }
            } else {
                System.out.println("txHash is exit");
            }
        };
        scheduler.schedule(task, Instant.now().plusSeconds(120));
    }
}
